﻿using Masuit.Tools.DateTimeExt;
using System.Text.RegularExpressions;

namespace MhAdmin.SqlSugar;

public class RulesUtils : IRulesUtils
{
    private readonly ISqlSugarClient Context;
    private readonly ITenant iTenant;
    private readonly ILogger<RulesUtils> _logger;

    public RulesUtils(ILogger<RulesUtils> logger)
    {
        Context = DbContext.Db.GetConnectionScopeWithAttr<DevRules>();//ioc注入的对象
        iTenant = DbContext.Db;
        _logger = logger;
    }

    /// <summary>
    /// 生成一个流水号
    /// </summary>
    /// <param name="serialCode">流水号编码</param>
    /// <returns></returns>
    /// <exception cref="RuleException"></exception>
    public string Next(string serialCode) => GenerateFlowsAsync(serialCode, 1).ConfigureAwait(false).GetAwaiter().GetResult().First();

    /// <summary>
    /// 生成一个流水号
    /// </summary>
    /// <param name="serialCode">流水号编码</param>
    /// <param name="code">唯一编码</param>
    public void Next(string serialCode, out string code)
    {
        code = GenerateFlowsAsync(serialCode, 1).ConfigureAwait(false).GetAwaiter().GetResult().First();
    }

    /// <summary>
    /// 生成流水号
    /// </summary>
    /// <param name="serialCode">流水号编码</param>
    /// <param name="length">长度</param>
    /// <returns></returns>
    /// <exception cref="RuleException"></exception>
    public RuleEnumerator<string> NextArray(string serialCode, int length) => new(GenerateFlowsAsync(serialCode, length).ConfigureAwait(false).GetAwaiter().GetResult());

    /// <summary>
    /// 生成一个流水号
    /// </summary>
    /// <param name="serialCode">流水号编码</param>
    /// <returns></returns>
    /// <exception cref="RuleException"></exception>
    public async Task<string> NextAsync(string serialCode) => (await GenerateFlowsAsync(serialCode, 1)).First();

    /// <summary>
    /// 生成流水号
    /// </summary>
    /// <param name="serialCode">流水号编码</param>
    /// <param name="length">长度</param>
    /// <returns></returns>
    /// <exception cref="RuleException"></exception>
    public async Task<RuleEnumerator<string>> NextArrayAsync(string serialCode, int length) => new RuleEnumerator<string>(await GenerateFlowsAsync(serialCode, length));

    /// <summary>
    /// 生成编号
    /// </summary>
    /// <param name="ruleCode">规则编号</param>
    /// <param name="length">长度 -> 不可为0</param>
    /// <returns></returns>
    /// <exception cref="RuleException"></exception>
    private async Task<List<string>> GenerateFlowsAsync(string ruleCode, int length)
    {
        // 检查规则编号是否合法
        if (string.IsNullOrEmpty(ruleCode)) throw new RuleException("RuleCode 不能为空");
        if (length <= 0) throw new RuleException("长度不可为0");

        // 获取编号
        var rules = await Context.Queryable<DevRules>().Where(x => x.RuleCode == ruleCode).FirstAsync();
        if (rules == null || rules.LastLen == default) throw new RuleException("规则编号不存在");

        // 获取格式化日期
        string formatDate = string.IsNullOrEmpty(rules.MiddleTime) ? "" : ToFormatSimple(DateTime.Now, rules.MiddleTime);

        // 开始事务
        await Context.AsTenant().BeginTranAsync();

        try
        {
            // 查看日志
            var log = await Context.Queryable<DevRulesLog>()
                   .TranLock(DbLockType.Wait)
                   .Where(x => x.RuleCode == ruleCode)
                   .WhereIF(!string.IsNullOrEmpty(formatDate), x => x.UsedDate == formatDate)
                   .OrderByDescending(x => x.Timestamp)
                   .FirstAsync();

            if (((log?.Used ?? rules.StartValue) + length).ToString().Length > rules.LastLen) throw new RuleException("编号后缀长度不足");

            long begin;
            if (log == null) // 新日志
            {
                // 插入新日志
                DevRulesLog newLog = new()
                {
                    RuleCode = ruleCode,
                    UsedDate = formatDate,
                    Used = rules.StartValue + length - 1,
                    Timestamp = DateTime.Now.GetTotalMilliseconds()
                };

                // 更新数据库
                await Context.Insertable(newLog).ExecuteCommandAsync();

                // 设置编码起始值
                begin = rules.StartValue;
            }
            else // 老日志
            {
                // 修改日志使用量
                await Context.Updateable<DevRulesLog>()
                    .SetColumns(x => x.Used == x.Used + length)
                    .Where(x => x.Id == log.Id)
                    .ExecuteCommandAsync();

                // 设置编码起始值
                begin = log.Used + 1;
            }

            // 提交事务
            await Context.AsTenant().CommitTranAsync();

            // 生成流水
            List<string> flows = new();
            for (long i = 0; i < length; i++)
            {
                // 补全编码
                string cur = (begin + i).ToString().PadLeft(rules.LastLen, '0');

                // 添加进集合
                flows.Add(rules.Header + formatDate + cur);
            }

            return flows;
        }
        catch (Exception ex)
        {
            // 回滚事务
            await Context.AsTenant().RollbackTranAsync();

            // 记录日志
            _logger.LogError(ex, "生成编号{ruleCode}失败", ruleCode);

            // 重新抛出异常
            throw;
        }
    }

    /// <summary>
    /// 格式化字符串, 简单处理
    /// </summary>
    /// <param name="dateTime"></param>
    /// <param name="format"></param>
    /// <returns></returns>
    public static string ToFormatSimple(DateTime dateTime, string format)
    {
        // 在调用ToString之前替换特殊占位符
        string result = Regex.Replace(format, @"WW", dateTime.WeekOfYear(DayOfWeek.Monday).ToString());
        result = Regex.Replace(result, @"ww", dateTime.WeekOfYear().ToString());

        return dateTime.ToString(result);
    }
}
